package cn.yeziji.utils;

import cn.yeziji.common.GResult;
import cn.yeziji.entity.MinIoFileEntity;
import cn.yeziji.entity.args.MinioComposeArgs;
import io.minio.*;
import io.minio.errors.*;
import io.minio.messages.Bucket;
import io.minio.messages.DeleteObject;
import io.minio.messages.Item;
import lombok.SneakyThrows;
import org.apache.http.protocol.HTTP;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.net.URLDecoder;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.time.LocalDateTime;
import java.time.ZoneId;
import java.time.ZonedDateTime;
import java.util.*;

/**
 * @author gzkemays
 * @date 2021/4/17 22:07
 */
public class MinIoOperaUtils {
  private static MinioClient CLIENT;
  private static String END_POINT;
  public static final String MB = "MB";
  public static final String GB = "GB";

  public static long CUSTOM_SIZE(int size, String format) {
    if (Objects.equals(format, MB)) {
      return size * 1024 * 1024;
    }
    if (Objects.equals(format, GB)) {
      return size * 1024 * 1024 * 1024;
    }
    return size * 1024;
  }
  /**
   * 初始化 MinIoClient
   *
   * @param accessKey access 密钥（账号）
   * @param secretKey secret 密钥（密码）
   * @param endPoint 连接 minio 的接口地址
   */
  public MinIoOperaUtils(String accessKey, String secretKey, String endPoint) {
    CLIENT = MinioClient.builder().credentials(accessKey, secretKey).endpoint(endPoint).build();
    END_POINT = endPoint;
  }

  /**
   * 上传文件
   *
   * @param bucket 桶名
   * @param folder 文件夹
   * @param file 文件
   * @return
   */
  public GResult uploadFile(String bucket, String folder, MultipartFile file) {
    String objName = "";
    try {
      if (!CLIENT.bucketExists(BucketExistsArgs.builder().bucket(bucket).build())) {
        CLIENT.makeBucket(MakeBucketArgs.builder().bucket(bucket).build());
        CLIENT.setBucketPolicy(SetBucketPolicyArgs.builder().bucket(bucket).build());
      }
      SimpleDateFormat sdf = new SimpleDateFormat(DateFormatUtils.NORMAL2_FORMAT);
      // 文件存储的结构目录
      if (Objects.isNull(folder)) {
        objName = sdf.format(new Date()) + "/" + file.getOriginalFilename();
      } else {
        objName = folder + "/" + file.getOriginalFilename();
      }
      // 上传文件
      CLIENT.putObject(
          PutObjectArgs.builder().bucket(bucket).object(objName).stream(
                  file.getInputStream(), file.getSize(), PutObjectArgs.MIN_MULTIPART_SIZE)
              .contentType(file.getContentType())
              .build());
    } catch (IOException
        | InvalidKeyException
        | InvalidResponseException
        | InsufficientDataException
        | NoSuchAlgorithmException
        | ServerException
        | InternalException
        | XmlParserException
        | ErrorResponseException e) {
      e.printStackTrace();
    }
    if (!objName.isEmpty()) {
      return GResult.init().code(1).data(END_POINT + "/" + bucket + "/" + objName).build();
    }
    return GResult.init().code(0).build();
  }

  public GResult deleteFile(String bucket, String path) {
    try {
      CLIENT.removeObject(RemoveObjectArgs.builder().bucket(bucket).object(path).build());
    } catch (ErrorResponseException
        | InsufficientDataException
        | InternalException
        | InvalidKeyException
        | InvalidResponseException
        | IOException
        | NoSuchAlgorithmException
        | ServerException
        | XmlParserException e) {
      e.printStackTrace();
    }
    return GResult.init().code(1).msg("删除成功").build();
  }
  /**
   * 获取 bucket 文件列表
   *
   * @return
   */
  public GResult getBucketList() {
    try {
      List<Bucket> buckets = CLIENT.listBuckets();
      List<MinIoFileEntity> fileVos = new ArrayList<>();
      DecimalFormat df = new DecimalFormat("0.00");
      if (!buckets.isEmpty()) {
        buckets.forEach(
            bucket -> {
              Iterable<Result<Item>> results =
                  CLIENT.listObjects(ListObjectsArgs.builder().bucket(bucket.name()).build());
              results.forEach(
                  item -> {
                    try {
                      MinIoFileEntity build =
                          MinIoFileEntity.builder()
                              .bucketName(URLDecoder.decode(bucket.name(), "UTF-8"))
                              .fileName(URLDecoder.decode(item.get().objectName(), "UTF-8"))
                              .updateTime(
                                  localDateTime2Date(item.get().lastModified().toLocalDateTime()))
                              .build();
                      long size = item.get().size();
                      if (size > (CUSTOM_SIZE(1, GB))) {
                        build.setFileSize(df.format(((double) size / 1024 / 1024 / 1024)) + GB);
                      } else if (size > CUSTOM_SIZE(1, MB)) {
                        build.setFileSize(df.format(((double) size / 1024 / 1024)) + MB);
                      } else if (size > CUSTOM_SIZE(1, null)) {
                        build.setFileSize(df.format(((double) size / 1024)) + "KB");
                      } else {
                        build.setFileSize(size + "bytes");
                      }
                      fileVos.add(build);
                    } catch (ErrorResponseException
                        | InvalidKeyException
                        | InvalidResponseException
                        | InternalException
                        | InsufficientDataException
                        | IOException
                        | NoSuchAlgorithmException
                        | ServerException
                        | XmlParserException e) {
                      e.printStackTrace();
                    }
                  });
            });
        return GResult.init().data(fileVos).build();
      }
    } catch (IOException
        | InvalidKeyException
        | InvalidResponseException
        | InsufficientDataException
        | NoSuchAlgorithmException
        | ServerException
        | InternalException
        | XmlParserException
        | ErrorResponseException ignored) {
      ignored.printStackTrace();
    }
    return GResult.init().msg("没有bucket").build();
  }

  /**
   * 合并上传文件
   *
   * @param args minio 合并文件参数对象 {@link MinioComposeArgs}
   * @return {@code GResult} 如果无异常返回 Data 为 {@code True}
   */
  @SneakyThrows
  public void composeChuckFile(MinioComposeArgs args) {
    Iterable<Result<Item>> results =
        CLIENT.listObjects(
            ListObjectsArgs.builder()
                .bucket(args.getChuckBucket())
                .prefix(args.getChuckFolder() + "/" + args.getChuckPrefix())
                .recursive(true)
                .build());
    List<ComposeSource> objs = new ArrayList<>();
    for (Result<Item> result : results) {
      if (args.isUseDecode()) {
        objs.add(
            ComposeSource.builder()
                .bucket(args.getChuckBucket())
                .object(URLDecoder.decode(result.get().objectName()))
                .build());
      } else {
        objs.add(
            ComposeSource.builder()
                .bucket(args.getChuckBucket())
                .object(result.get().objectName())
                .build());
      }
    }
    // 添加转化类型
    Map<String, String> header =
        new HashMap<String, String>(1) {
          {
            put(HTTP.CONTENT_TYPE, args.getContentType());
          }
        };
    // 开始合并文件
    CLIENT.composeObject(
        ComposeObjectArgs.builder()
            .bucket(args.getComposeBucket())
            .object(args.getComposeFolder() + "/" + args.getComposeFileName())
            .headers(header)
            .sources(objs)
            .build());
    // 是否删除分片信息
    if (args.isDeleteChuck()) {
      List<DeleteObject> deleteObjects = new ArrayList<>(objs.size());
      for (ComposeSource obj : objs) {
        deleteObjects.add(new DeleteObject(obj.object()));
      }
      CLIENT.removeObjects(
          RemoveObjectsArgs.builder().bucket(args.getChuckBucket()).objects(deleteObjects).build());
    }
  }

  /** 合并 minio 上的文件 */
  public GResult composeFile(String bucket, String objectName, List<ComposeSource> sources) {
    try {
      CLIENT.composeObject(
          ComposeObjectArgs.builder().bucket(bucket).object(objectName).sources(sources).build());
    } catch (ErrorResponseException
        | InsufficientDataException
        | InternalException
        | InvalidKeyException
        | InvalidResponseException
        | IOException
        | NoSuchAlgorithmException
        | ServerException
        | XmlParserException e) {
      e.printStackTrace();
    }
    return GResult.init().msg("合并成功").build();
  }
  /**
   * LocalDateTime转换为Date
   *
   * @param localDateTime 时间转换
   */
  public String localDateTime2Date(LocalDateTime localDateTime) {
    // 获取当前系统的时区 id
    ZoneId zoneId = ZoneId.systemDefault();
    ZonedDateTime zdt = localDateTime.atZone(zoneId);
    Date date = Date.from(zdt.toInstant());
    Calendar cal = Calendar.getInstance();
    cal.setTime(date);
    // 由于获取的时间存在时间差，我这里手动加上16小时
    cal.add(Calendar.HOUR_OF_DAY, 16);
    date = cal.getTime();
    return DateFormatUtils.dateToStr(date, DateFormatUtils.NORMAL_FORMAT);
  }
}
